# -*- coding: utf-8 -*-
"""
learn.dict_functions
| @brief 
| @author Zhichao Wu
| @date 2022-04-26 23:14:30
| @version 1.0.0
"""
import copy
from collections import defaultdict, OrderedDict
from datetime import datetime
import unittest


class MyDict(dict):
    """自定义字典：方便研究字典某些方法"""

    def __copy__(self):
        """
        重写 copy.copy
        :return: dict
        """
        result = {'__old__': id(self)}
        result.update(self.items())
        return result

    def __missing__(self, key):
        print(f'键"{key}"不存在.')
        self.setdefault(key, None)

    def __delitem__(self, key):
        print(f'删除键:{key}.')
        super(MyDict, self).__delitem__(key)

    def __getitem__(self, item):
        print(f'获取键:{item}.')
        return super(MyDict, self).__getitem__(item)

    def __setitem__(self, key, value):
        print(f'设置键:{key}={value}.')
        super(MyDict, self).__setitem__(key, value)


class DictFunctionsTestCase(unittest.TestCase):

    def setUp(self) -> None:
        self.dictionary: dict = {'str': 'abc', 'int': 1, 'list': ['a', 1]}
        self.default_dict: defaultdict = defaultdict(list, **{'str': 'abc', 'int': 1, 'list': ['a', 1]})
        self.ordered_dict: OrderedDict = OrderedDict({'str': 'abc', 'int': 1, 'list': ['a', 1]})

    def test_clear(self):
        self.assertTrue(len(self.dictionary) > 0)
        self.dictionary.clear()
        self.assertEqual(0, len(self.dictionary))

        self.assertTrue(len(self.default_dict) > 0)
        self.default_dict.clear()
        self.assertEqual(0, len(self.default_dict))

        self.assertTrue(len(self.ordered_dict) > 0)
        self.ordered_dict.clear()
        self.assertEqual(0, len(self.ordered_dict))

    def test___contains__(self):
        self.assertTrue('str' in self.dictionary)
        self.assertTrue('str' in self.default_dict)
        self.assertTrue('str' in self.ordered_dict)

        self.assertFalse('1' in self.dictionary)
        self.assertFalse('1' in self.default_dict)
        self.assertFalse('1' in self.ordered_dict)

    def test_copy(self):
        actual1 = self.dictionary.copy()
        self.assertDictEqual(self.dictionary, actual1)
        actual1['int'] = 2
        self.assertNotEqual(actual1.get('int'), self.dictionary.get('int'))
        actual1['list'].append(2)
        self.assertEqual(actual1.get('list'), self.dictionary.get('list'))

        actual2 = self.default_dict.copy()
        self.assertDictEqual(self.default_dict, actual2)
        actual2['int'] = 2
        self.assertNotEqual(actual2.get('int'), self.default_dict.get('int'))
        actual2['list'].append(2)
        self.assertEqual(actual2.get('list'), self.default_dict.get('list'))

        actual3 = self.ordered_dict.copy()
        self.assertDictEqual(self.ordered_dict, actual3)
        actual3['int'] = 2
        self.assertNotEqual(actual3.get('int'), self.ordered_dict.get('int'))
        actual3['list'].append(2)
        self.assertEqual(actual3.get('list'), self.ordered_dict.get('list'))

    def test__function(self):
        d1 = MyDict(**self.dictionary)

        self.assertEqual(len(self.dictionary), len(d1))
        for k, v in self.dictionary.items():
            if isinstance(v, list):
                self.assertListEqual(v, d1[k])
            else:
                self.assertEqual(v, d1[k])

        d2 = copy.copy(d1)
        self.assertEqual(len(d2), len(d1) + 1)
        self.assertSetEqual({'__old__'}, d2.keys() - d1.keys())

        self.assertIsNone(d1['a'])
        d1['a'] = 1
        self.assertEqual(len(self.dictionary) + 1, len(d1))

        del d1['a']
        self.assertEqual(len(self.dictionary), len(d1))

    def test_update(self):
        d1 = {1: 1, 2: 2}
        self.dictionary.update(d1)
        self.assertEqual(d1[1], self.dictionary[1])

        self.default_dict.update(d1)
        self.assertEqual(d1[1], self.default_dict[1])

        self.ordered_dict.update(d1)
        self.assertEqual(d1[1], self.ordered_dict[1])

        d2 = [(1, 101), (2, 102)]
        self.dictionary.update(d2)
        self.assertEqual(101, self.dictionary[1])

        self.default_dict.update(d2)
        self.assertEqual(101, self.default_dict[1])

        self.ordered_dict.update(d2)
        self.assertEqual(101, self.ordered_dict[1])

    def test_setdefault(self):
        d1 = {}
        d2 = {}

        for i in range(3):
            if 'key' not in d1:
                d1['key'] = []
            d1['key'].append(i)
            # 等价于
            d2.setdefault('key', []).append(i)

        self.assertDictEqual(d1, d2)


if __name__ == '__main__':
    start_time = datetime.now()
    print("[%s]%s开始运行..." % (start_time.strftime('%Y-%m-%d %H:%M:%S.%f'), __file__))

    unittest.main()

    end_time = datetime.now()
    print(f"[{end_time.strftime('%Y-%m-%d %H:%M:%S.%f')}]{__file__}结束运行.")
    print(f"耗时:{end_time - start_time}")
